package blyth;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;

public class Astar {

    public int maxClosed = 300;
    private World world;
    ArrayList<Node> openList;
    ArrayList<Node> closedList;

    public Astar(World world) {
        this.world = world;
        this.openList = new ArrayList<Node>();
        this.closedList = new ArrayList<Node>();
    }

    public Node NextStep(Point start, Point goal) {
        if (start.equals(goal)) {
            // Shouldn't occur
            return null;
        }
        // Initialize
        openList.clear();
        closedList.clear();
        Node startNode = new Node(start, null, 0, goal);
        openList.add(startNode);
        boolean goalFound = false;
        while (goalFound == false && closedList.size() < maxClosed) {
            if (openList.isEmpty()) {
                // Shouldn't occur
                return null;
            }
            // Select lowest cost Node from open List
            int lowestCost = -1, currentCost = 0;
            Node selectedNode = null, currentNode = null;
            Iterator<Node> it = openList.iterator();
            while (it.hasNext()) {
                currentNode = it.next();
                currentCost = currentNode.cost + currentNode.heuristics;
                if (lowestCost == -1 || currentCost < lowestCost) {
                    selectedNode = currentNode;
                    lowestCost = currentCost;
                }
            }
            // Add neighbours to open list / refresh better ways
            Point p = new Point();
            for (WorldIterator wit = new WorldIterator(world, selectedNode.point.x,
                    selectedNode.point.y, 1, false, 1); wit.IsNotEnd(); wit.Next()) {
                p.setLocation(wit.x, wit.y);
                if (world.IsThereEnemy(p) == false) {
                    if (IsOnClosed(p) == false) {
                        int newCost = selectedNode.cost
                                + world.tile[selectedNode.point.x][selectedNode.point.y].getMovementCost();
                        if (IsOnOpen(p, selectedNode, newCost) == false) {
                            openList.add(new Node((Point) p.clone(), selectedNode, newCost, goal));
                        }
                    }
                }
            }
            // Move self to closed
            openList.remove(selectedNode);
            closedList.add(selectedNode);
            // Was it the goal?
            if (selectedNode.heuristics == 0) {
                goalFound = true;
            }
        }
        if (closedList.isEmpty()) {
            // Shouldn't occur
            return null;
        }
        // Select closest Node to the goal
        return GetBestNode(start);        
    }

    private boolean IsOnClosed(Point p) {
        boolean isOnClosed = false;
        Iterator<Node> it = closedList.iterator();
        while (it.hasNext() && isOnClosed == false) {
            if (it.next().point.equals(p)) {
                isOnClosed = true;
            }
        }
        return isOnClosed;
    }

    // This also refreshes the open list
    private boolean IsOnOpen(Point p, Node from, int newCost) {
        boolean isOnOpen = false;
        Node currentNode = null;
        Iterator<Node> it = openList.iterator();
        while (it.hasNext() && isOnOpen == false) {
            currentNode = it.next();
            if (currentNode.point.equals(p)) {
                isOnOpen = true;
                if (currentNode.cost > newCost) {
                    currentNode.cost = newCost;
                    currentNode.reachedFrom = from;
                }
            }
        }
        return isOnOpen;
    }

    private Node GetBestNode(Point start) {
        int lowestHeuristics = -1;
        Node bestNode = null, currentNode = null;
        Iterator<Node> it = closedList.iterator();
        while (it.hasNext()) {
            currentNode = it.next();
            if ((lowestHeuristics == -1 || currentNode.heuristics < lowestHeuristics
                    || (currentNode.heuristics == lowestHeuristics && currentNode.cost < bestNode.cost))
                    && currentNode.point.equals(start) == false) {
                bestNode = currentNode;
                lowestHeuristics = bestNode.heuristics;
            }
        }
        return bestNode;
    }
}